//
//  LevelScene.cpp
//  NinjaTurtle
//
//  Created by Nguyen Tri Chung on 2/16/15.
//
//

#include "LevelScene.h"

LevelScene::LevelScene() {
    this->currentCommandIndex = 0;
    this->introTurtle = NULL;
    this->preparingPlayGame = false;
    this->inialize();
}

LevelScene::~LevelScene() {
}

void LevelScene::disposeComponents() {
    CC_SAFE_RELEASE(this->introTurtle);
    this->unscheduleUpdate();
    this->eventListeners->clear();
    
    GameModel* gameModel = GameModel::getInstance();
    gameModel->removeEventListener(this->gameEventListener);
    
    this->_eventDispatcher->removeEventListener(this->touchListener);
    this->_eventDispatcher->removeEventListener(this->keyBoardListener);
}

void LevelScene::onExit() {
    this->removeAllChildren();
}

void LevelScene::inialize() {
    GameModel* gameModel = GameModel::getInstance();
    this->gameEventListener = new GameEventListener();
    this->gameEventListener->setHandlerObject(this);
    this->gameEventListener->setHandlerFunction(handler_function(LevelScene::processEvent));
    gameModel->addEventListener(this->gameEventListener);
    
    this->touchListener = EventListenerTouchOneByOne::create();
    this->touchListener->onTouchBegan = CC_CALLBACK_2(LevelScene::onTouchBegan, this);
    this->touchListener->onTouchMoved = CC_CALLBACK_2(LevelScene::onTouchMoved, this);
    this->touchListener->onTouchEnded = CC_CALLBACK_2(LevelScene::onTouchEnded, this);
    this->touchListener->onTouchCancelled = CC_CALLBACK_2(LevelScene::onTouchEnded, this);
    this->_eventDispatcher->addEventListenerWithSceneGraphPriority(this->touchListener, this);
    
    this->keyBoardListener = EventListenerKeyboard::create();
    this->keyBoardListener->onKeyPressed = CC_CALLBACK_2(LevelScene::onKeyPress, this);
    this->keyBoardListener->onKeyReleased = CC_CALLBACK_2(LevelScene::onKeyRelease, this);
    this->_eventDispatcher->addEventListenerWithSceneGraphPriority(this->keyBoardListener, this);
    
    this->createLayers();
    this->createLayersContent();
}

void LevelScene::createLayers() {
    this->backgroundLayer = Layer::create();
    this->addChild(this->backgroundLayer);
    
    this->guiLayer = Layer::create();
    this->addChild(this->guiLayer);
    
    this->dialogLayer = new TextDialogLayer(this);
    this->addChild(this->dialogLayer);
    this->dialogLayer->setVisible(false);
}

void LevelScene::createLayersContent() {
    GameModel* gameModel = GameModel::getInstance();
    Size winSize = Director::getInstance()->getWinSize();
    
    // create background
    CSprite* mapBackground1 = new CSprite();
    mapBackground1->initWithSpriteFrameName(FRAME_BG_1);
    mapBackground1->setPosition(winSize.width / 2, winSize.height / 2);
    this->backgroundLayer->addChild(mapBackground1);
    
    CSprite* mapBackground2 = new CSprite();
    mapBackground2->initWithSpriteFrameName(FRAME_BG_2);
    mapBackground2->setPosition(winSize.width / 2, winSize.height / 2);
    this->backgroundLayer->addChild(mapBackground2);
    
    CSprite* mapBackground3 = new CSprite();
    mapBackground3->initWithSpriteFrameName(FRAME_BG_3);
    mapBackground3->setPosition(winSize.width / 2, winSize.height / 2);
    this->backgroundLayer->addChild(mapBackground3);
    
    // create title
    this->titleLabel = CNodeUtil::createLabelTTF(TXT_TITLE, 12, FONT_ARCADE);
    this->titleLabel->setPosition(winSize.width / 2, winSize.height - this->titleLabel->getContentSize().height / 2 - 10);
    this->guiLayer->addChild(this->titleLabel);
    this->titleLabel->setOpacity(0);
    
    // create logo
    this->logoFace = new CSprite();
    this->logoFace->initWithSpriteFrameName(FRAME_LOGO);
    this->logoFace->setPosition(winSize.width / 2, winSize.height + 100);
    this->guiLayer->addChild(this->logoFace);
    
    this->targetJumpHeights.push_back(0);
    this->targetJumpHeights.push_back(20);
    this->targetJumpHeights.push_back(10);
    this->targetJumpHeights.push_back(5);
    
    this->currentTargetJumpHeightIndex = 0;
    this->currentJumpTime = 0;
    this->logoLandPos = Point(winSize.width / 2, this->titleLabel->getPositionY() - titleLabel->getContentSize().height / 2 - this->logoFace->getContentSize().height / 2 - 10);
    this->lastStepY = this->logoFace->getPosition().y;
    
    // create command button
    int firstCommand = 0;
    if (gameModel->getSavedData()->getLevel() != -1) {
        this->commandList.push_back(TXT_CONTINUE);
        firstCommand = COMMAND_CONTINUE;
    } else {
        firstCommand = COMMAND_NEW_GAME;
    }
    this->commandList.push_back(TXT_NEW_GAME);
    this->commandList.push_back(TXT_MUSIC);
    this->commandList.push_back(TXT_EFFECT);
    
    this->commandButton = CNodeUtil::createLabelTTF(this->commandList.at(this->currentCommandIndex), 18, FONT_ARCADE);
    this->commandButton->setPosition(winSize.width / 2, 50);
    this->commandButton->setTag(firstCommand);
    this->commandButton->setOpacity(0);
    {
        auto colorAction = Sequence::create(TintTo::create(0.1f, 255, 0, 0),
                                            TintTo::create(0.1f, 0, 255, 0),
                                            TintTo::create(0.1f, 0, 0, 255),
                                            TintTo::create(0.1f, 0, 255, 255),
                                            TintTo::create(0.1f, 255, 255, 0),
                                            TintTo::create(0.1f, 255, 0, 255),
                                            TintTo::create(0.1f, 255, 255, 255),
                                            CallFunc::create(CC_CALLBACK_0(LevelScene::onFunctionCallback, this, FUNCTION_CALL_BACK_PLAY_GAME)),
                                            NULL);
        this->commandButton->setUserObject(colorAction);
    }
    
    this->previousCommand = new CSprite();
    this->previousCommand->initWithSpriteFrameName(FRAME_PREVIOUS_COMMAND);
    this->previousCommand->setPosition(this->commandButton->getPosition() - Point(100, 0));
    this->previousCommand->setOpacity(0);
    
    this->nextCommand = new CSprite();
    this->nextCommand->initWithSpriteFrameName(FRAME_NEXT_COMMAND);
    this->nextCommand->setPosition(this->commandButton->getPosition() + Point(100, 0));
    this->nextCommand->setOpacity(0);
    
    // add to layer
    this->guiLayer->addChild(this->commandButton);
    this->guiLayer->addChild(this->previousCommand);
    this->guiLayer->addChild(this->nextCommand);
}

void LevelScene::processEvent(GameEvent* event) {
    
}

void LevelScene::update(float dt) {
    if (this->currentTargetJumpHeightIndex < this->targetJumpHeights.size()) {
        float minY = 0;
        float maxY = this->targetJumpHeights.at(this->currentTargetJumpHeightIndex);
        float lengthJump = minY - maxY;
        
        this->currentJumpTime += dt;
        float velocityY = sqrt(2 *  lengthJump * -CONFIG_ACCEL_WORLD);
        float elapsedTime = this->currentJumpTime;
        float additionalHeight = velocityY * elapsedTime + ((-CONFIG_ACCEL_WORLD * elapsedTime * elapsedTime) / 2);
        
        float newPositionY = this->lastStepY + additionalHeight;
        if (newPositionY < this->logoLandPos.y) {
            newPositionY = this->logoLandPos.y;
            this->lastStepY = newPositionY;
            this->currentJumpTime = 0;
            this->currentTargetJumpHeightIndex++;
            
            GameEvent* gameEvent = new GameEvent();
            gameEvent->setEventCode(EVT_LOGO_TOUCH_LAND);
            this->fireEvent(gameEvent);
            
            if (this->currentTargetJumpHeightIndex == this->targetJumpHeights.size()) {
                GameEvent* gameEvent2 = new GameEvent();
                gameEvent2->setEventCode(EVT_LOGO_LAND);
                this->fireEvent(gameEvent2);
                
                this->titleLabel->runAction(FadeIn::create(0.5f));
                this->commandButton->runAction(FadeIn::create(0.5f));
                this->previousCommand->runAction(FadeIn::create(0.5f));
                this->nextCommand->runAction(FadeIn::create(0.5f));
                
                this->introTurtle = new Turtle();
                this->introTurtle->setJumping(true);
                this->introTurtle->getBody()->setScale(2);
                CC_SAFE_RETAIN(this->introTurtle);
                this->introTurtle->setPosition(this->logoFace->getPosition());
                this->addSpriteToLayer(this->introTurtle, this->backgroundLayer, 1);
                this->introTurtleLandPos = this->introTurtle->getPosition() - Point(0, 150);
            }
        }
        
        this->logoFace->setPositionY(newPositionY);
    }
    
    if (this->introTurtle != NULL) {
        this->introTurtle->update(dt);
        
        if (this->introTurtle->getJumping()) {
            float lengthJump = 0;
            this->introTurtle->setCurrentElapsedJumpTime(this->introTurtle->getCurrentElapsedJumpTime() + dt);
            float velocityY = sqrt(2 *  lengthJump * -CONFIG_INTRO_ACCEL_WORLD);
            float elapsedTime = this->introTurtle->getCurrentElapsedJumpTime();
            float additionalHeight = velocityY * elapsedTime + ((-CONFIG_INTRO_ACCEL_WORLD * elapsedTime * elapsedTime) / 2);
            
            if (this->introTurtle->getLastStepY() == -9999) {
                this->introTurtle->setLastStepY(this->introTurtle->getPosition().y);
            }
            
            float newPositionY = this->introTurtle->getLastStepY() + additionalHeight;
            this->introTurtle->setPosition(Point(this->introTurtle->getPosition().x, newPositionY));
            
            if (newPositionY < this->introTurtleLandPos.y) {
                this->introTurtle->setPosition(Point(this->introTurtle->getPosition().x, this->introTurtleLandPos.y));
                this->introTurtle->playLoop(SPRITE_TYPE_MAIN, FRAME_TURTLE_MOVE);
                this->introTurtle->setJumping(false);
            } else {
                this->introTurtle->play(SPRITE_TYPE_MAIN, FRAME_TURTLE_FALL);
            }
        }
    }
}

void LevelScene::onFunctionCallback(int code) {
    if (code == FUNCTION_CALL_BACK_OK_NEW_GAME) {
        GameEvent* clickButtonEvent = new GameEvent();
        clickButtonEvent->setEventCode(EVT_CLICK_BUTTON);
        this->fireEvent(clickButtonEvent);
        
        GameEvent* gameEvent = new GameEvent();
        gameEvent->setEventCode(EVT_RESET_GAME);
        this->fireEvent(gameEvent);
        
        DataEvent* dataEvent = new DataEvent();
        dataEvent->setEventCode(EVT_PLAY_GAME);
        dataEvent->setArgumentInt(1);
        this->fireEvent(dataEvent);
    } else if (code == FUNCTION_CALL_BACK_CLOSE_NEW_GAME) {
        GameEvent* clickButtonEvent = new GameEvent();
        clickButtonEvent->setEventCode(EVT_CLICK_BUTTON);
        this->fireEvent(clickButtonEvent);
        
        this->dialogLayer->hide();
    } else if (code == FUNCTION_CALL_BACK_PLAY_GAME) {
        DataEvent* dataEvent = new DataEvent();
        dataEvent->setEventCode(EVT_PLAY_GAME);
        dataEvent->setArgumentInt(this->commandButton->getTag());
        this->fireEvent(dataEvent);
    } else if (code == FUNCTION_CALL_BACK_YES_QUIT_GAME) {
        GameEvent* clickButtonEvent = new GameEvent();
        clickButtonEvent->setEventCode(EVT_CLICK_BUTTON);
        this->fireEvent(clickButtonEvent);
        
        GameEvent* gameEvent = new GameEvent();
        gameEvent->setEventCode(EVT_EXIT_GAME);
        this->fireEvent(gameEvent);
    } else if (code == FUNCTION_CALL_BACK_NO_QUIT_GAME) {
        GameEvent* clickButtonEvent = new GameEvent();
        clickButtonEvent->setEventCode(EVT_CLICK_BUTTON);
        this->fireEvent(clickButtonEvent);
        
        this->dialogLayer->hide();
    }
}

void LevelScene::onKeyPress(EventKeyboard::KeyCode keyCode, Event* event) {
}

void LevelScene::onKeyRelease(EventKeyboard::KeyCode keyCode, cocos2d::Event *event) {
    if (this->dialogLayer->isVisible()) {
        return;
    }
    
    if (keyCode == EventKeyboard::KeyCode::KEY_BACK) {
        GameEvent* clickButtonEvent = new GameEvent();
        clickButtonEvent->setEventCode(EVT_CLICK_BUTTON);
        this->fireEvent(clickButtonEvent);
        
        this->dialogLayer->showConfirmDialog(TXT_CONFIRM_QUIT_GAME_DESCRIPTION, TXT_CONFIRM_QUIT_GAME_TITLE, TXT_CONFIRM_QUIT_GAME_YES, TXT_CONFIRM_QUIT_GAME_NO);
        this->dialogLayer->getButton1()->setCallback(CC_CALLBACK_0(LevelScene::onFunctionCallback, this, FUNCTION_CALL_BACK_YES_QUIT_GAME));
        this->dialogLayer->getButton2()->setCallback(CC_CALLBACK_0(LevelScene::onFunctionCallback, this, FUNCTION_CALL_BACK_NO_QUIT_GAME));
    }
}

bool LevelScene::onTouchBegan(Touch* touch, Event* event) {
    return true;
}

void LevelScene::onTouchMoved(Touch* touch, Event* event) {
}

void LevelScene::onTouchEnded(Touch* touch, Event* event) {
    if (this->dialogLayer->isVisible()) {
        return;
    }
    
    if (this->introTurtle == NULL) {
        return;
    }
    
    if (this->preparingPlayGame) {
        return;
    }
    
    GameModel* gameModel = GameModel::getInstance();
    int savedLevel = gameModel->getSavedData()->getLevel();
    Point point = this->convertTouchToNodeSpace(touch);
    if (CPointUtil::isPointInRectangle(point, this->commandButton->getPosition(), this->commandButton->getContentSize().width, this->commandButton->getContentSize().height + 10)) {
        
        if (this->commandButton->getTag() == COMMAND_NEW_GAME) {
            if (savedLevel != -1) {
                GameEvent* clickButtonEvent = new GameEvent();
                clickButtonEvent->setEventCode(EVT_CLICK_BUTTON);
                this->fireEvent(clickButtonEvent);
                
                char confirmText[200];
                sprintf(confirmText, TXT_CONFIRM_NEW_GAME_DESCRIPTION, savedLevel);
                
                this->dialogLayer->showConfirmDialog(confirmText, TXT_CONFIRM_NEW_GAME_TITLE, TXT_CONFIRM_NEW_GAME_OK, TXT_CONFIRM_NEW_GAME_CLOSE);
                
                
                this->dialogLayer->getButton1()->setCallback(CC_CALLBACK_0(LevelScene::onFunctionCallback, this, FUNCTION_CALL_BACK_OK_NEW_GAME));
                this->dialogLayer->getButton2()->setCallback(CC_CALLBACK_0(LevelScene::onFunctionCallback, this, FUNCTION_CALL_BACK_CLOSE_NEW_GAME));
            } else {
                GameEvent* selectEvent = new GameEvent();
                selectEvent->setEventCode(EVT_SELECT);
                this->fireEvent(selectEvent);
                
                this->commandButton->setTag(1);
                this->preparingPlayGame = true;
                this->commandButton->runAction((Action*) this->commandButton->getUserObject());
            }
        } else if (this->commandButton->getTag() == COMMAND_CONTINUE) {
            GameEvent* selectEvent = new GameEvent();
            selectEvent->setEventCode(EVT_SELECT);
            this->fireEvent(selectEvent);
            
            this->commandButton->setTag(savedLevel);
            this->preparingPlayGame = true;
            this->commandButton->runAction((Action*) this->commandButton->getUserObject());
        } else if (this->commandButton->getTag() == COMMAND_EFFECT) {
            GameEvent* clickButtonEvent = new GameEvent();
            clickButtonEvent->setEventCode(EVT_CLICK_BUTTON);
            this->fireEvent(clickButtonEvent);
            
            int effectOnInt = gameModel->getSavedData()->getEffectOn() ? 0 : 1;
            DataEvent* dataEvent = new DataEvent();
            dataEvent->setEventCode(EVT_CHANGE_EFFECT);
            dataEvent->setArgumentInt(effectOnInt);
            this->fireEvent(dataEvent);
            
            this->updateCommandText();
        } else if (this->commandButton->getTag() == COMMAND_MUSIC) {
            GameEvent* clickButtonEvent = new GameEvent();
            clickButtonEvent->setEventCode(EVT_CLICK_BUTTON);
            this->fireEvent(clickButtonEvent);
            
            int musicOnInt = gameModel->getSavedData()->getMusicOn() ? 0 : 1;
            DataEvent* dataEvent = new DataEvent();
            dataEvent->setEventCode(EVT_CHANGE_MUSIC);
            dataEvent->setArgumentInt(musicOnInt);
            this->fireEvent(dataEvent);
            
            this->updateCommandText();
        }
    } else if (CPointUtil::isPointInRectangle(point, this->previousCommand->getPosition(), CONFIG_COMMAND_PADDING, CONFIG_COMMAND_PADDING)) {
        GameEvent* clickCommandEvent = new GameEvent();
        clickCommandEvent->setEventCode(EVT_CHANGE_COMMAND);
        this->fireEvent(clickCommandEvent);
        
        this->changeCommand(false);
        this->updateCommandText();
    } else if (CPointUtil::isPointInRectangle(point, this->nextCommand->getPosition(), CONFIG_COMMAND_PADDING, CONFIG_COMMAND_PADDING)) {
        GameEvent* clickCommandEvent = new GameEvent();
        clickCommandEvent->setEventCode(EVT_CHANGE_COMMAND);
        this->fireEvent(clickCommandEvent);
        
        this->changeCommand(true);
        this->updateCommandText();
    }
}

void LevelScene::onTouchCancelled(Touch* touch, Event* event) {
    this->onTouchEnded(touch, event);
}

void LevelScene::changeCommand(bool next) {
    if (next) {
        this->currentCommandIndex++;
        if (this->currentCommandIndex > this->commandList.size() - 1) {
            this->currentCommandIndex = 0;
        }
    } else {
        this->currentCommandIndex--;
        if (this->currentCommandIndex < 0) {
            this->currentCommandIndex = this->commandList.size() - 1;
        }
    }
}

void LevelScene::updateCommandText() {
    string text = this->commandList.at(this->currentCommandIndex);
    int newCommand = 0;
    SavedData* savedData = GameModel::getInstance()->getSavedData();
    if (text == TXT_EFFECT) {
        char statusText[200];
        sprintf(statusText, text.data(), savedData->getEffectOn() ? TXT_ON : TXT_OFF);
        text = statusText;
        newCommand = COMMAND_EFFECT;
    } else if (text == TXT_MUSIC) {
        char statusText[200];
        sprintf(statusText, text.data(), savedData->getMusicOn() ? TXT_ON : TXT_OFF);
        text = statusText;
        newCommand = COMMAND_MUSIC;
    } else if (text == TXT_NEW_GAME) {
        newCommand = COMMAND_NEW_GAME;
    } else if (text == TXT_CONTINUE) {
        newCommand = COMMAND_CONTINUE;
    }
    
    this->commandButton->setString(text);
    this->commandButton->setTag(newCommand);
}

void LevelScene::addSpriteToLayer(Obj* object, Layer* layer, int index) {
    for (int i = 0; i < object->getSprites()->size(); i++) {
        CSprite* cSprite = (CSprite*) object->getSprites()->at(i);
        cSprite->setPosition(object->getPosition());
        cSprite->setAnchorPoint(object->getBody()->getAnchorPoint());
        cSprite->setFlippedX(object->getBody()->getFlipX());
        layer->addChild(cSprite, index);
    }
}